<script setup name="Simulator">
/**
 * v-md-editor组件注册
 */
import VMdPreview from '@kangc/v-md-editor/lib/preview';
import '@kangc/v-md-editor/lib/style/preview.css';
import githubTheme from '@kangc/v-md-editor/lib/theme/github.js';
import '@kangc/v-md-editor/lib/theme/style/github.css';
import hljs from 'highlight.js';
VMdPreview.use(githubTheme, {
  Hljs: hljs,
});

import { ref, reactive, toRefs, onMounted,nextTick } from 'vue'
import useUserStore from "@/store/modules/user.js";
import * as personality from "@/api/app/simulator.js";
import usePersonalityStore from "@/api/app/personality.js";
import {ElMessage, ElScrollbar, ElButton, ElMessageBox} from "element-plus";
import {Delete} from "@element-plus/icons-vue";
import {getToken} from "@/utils/auth.js";
import {EventSourcePolyfill} from "event-source-polyfill";

let personalityId = -1
const isPromptLoading = ref(false)
const botAvatar = "/src/assets/logo/bot.png"
const userStore = useUserStore() //用户信息相关Token
const personalityStore = usePersonalityStore() //人设相关Cookie
const formRef = ref()
const scrollbarRef = ref()
const data = reactive({
  formData:{
    msgInput: '',
  },
  chatHistory: [], //用于存储对话记录
  promptHistory: [], //用于存储人物设定描述记录
})

const {formData, chatHistory, promptHistory} = toRefs(data);

/**
 * 提交对话信息
 */
async function submitChatMessage(){
  formRef.value.validate(async (valid) => {
    if (!valid) return
    if (formData.value.msgInput === '' || formData.value.msgInput === undefined) return
    const userId = userStore.id;
    const userInput = formData.value.msgInput;
    const time = formattedTime(new Date());
    formData.value.msgInput = ''
    const userMessage = { role: 'user', content: userInput ,date: time }
    chatHistory.value.push(userMessage)
    const botMessage = { role: 'wait', content: '' }
    chatHistory.value.push(botMessage)

    const token = getToken();
    const baseUrl = import.meta.env.VITE_APP_BASE_API;
    const eventSource = new EventSourcePolyfill(`${baseUrl}/chat/sse/getStream/${userId}`,{
      headers: {'Authorization': `Bearer ${token}`},
      heartbeatTimeout: 60 * 1000,
    })
    eventSource.onopen = function (event){
      console.log('Connection start', event)
    }
    eventSource.onmessage = function (event){
      console.log('Received message:', event)
      const streamMessage = event.data;
      chatHistory.value.pop()
      const botMessage = { role: 'wait', content: streamMessage }
      chatHistory.value.push(botMessage)
    }
    eventSource.onerror = function(error) {
      console.log('EventSource closed:', error);
      handleChatHistory()
      eventSource.close();
    };
    eventSource.onclose = function (event) {
      console.log('EventSource closed:',event);
    }

    personality.sendChatMessage(userId, userInput).then(response => {
      if (response.code === 200) {
        handleChatHistory()
      }else{
        handleChatHistory()
        ElMessage.error('对话出现了一点意外')
      }
    })
  })
}

/**
 * 获取对话记录
 */
function handleChatHistory(){
  const userId = userStore.id;
  personality.getChatHistory(userId).then(response => {
    if (response.code === 200) {
      chatHistory.value = []
      if(response.hasOwnProperty('data')){
        response.data.forEach(item => {
          const role = (item.role==='user') ? 'user' : 'bot'
          const formatDate = formattedTime(item.date)
          const userMessage = {id:item.id, role: role, content: item.content, date: formatDate}
          chatHistory.value.push(userMessage)
        })
      }
    }
  })
}

/**
 * 根据userId删除对话记录
 */
function resetChat(){
  const userId = userStore.id;
  personality.delChatHistory(userId).then(response => {
    if (response.code === 200) {
      handleChatHistory()
    }else{
      ElMessage.error('重置对话出现了意外，请重试')
    }
  })
}

/**
 * 提交人设描述
 * @returns {Promise<void>}
 */
async function submitPrompt(){
  formRef.value.validate(async (valid) => {
    if (!valid) return
    if (formData.value.msgInput === '' || formData.value.msgInput === undefined) return
    const userId = userStore.id
    const userInput = formData.value.msgInput
    formData.value.msgInput = ''
    isPromptLoading.value = true
    personalityStore.sendPrompt(userId, userInput).then((response) => {
      if(response.code === 200) {
        handlePrompt()
        isPromptLoading.value = false
        updatePersonalityId()
      }
    }).catch(() => {
      isPromptLoading.value = false
      console.log('sendPrompt error')
    })
  })
}

/**
 * 更新人设描述记录
 */
function handlePrompt(){
  const userId = userStore.id
  personality.getCharacterPrompt(userId).then(response => {
    if(response.code === 200){
      promptHistory.value = []
      if(response.hasOwnProperty('data')){
        response.data.forEach(item => {
          const formatDate = formattedDate(item.date)
          console.log(formatDate)
          const prompt = {id: item.id, prompt: item.prompt, date: formatDate}
          promptHistory.value.push(prompt)
        })
      }
    }
  })
}

/**
 * 根据id删除人设描述
 */
function deletePrompt(id){
  const userId = userStore.id
  personality.delPromptById(id, userId).then(response => {
    if (response.code === 200){
      handlePrompt()
    }
  })
}

/**
 * 删除人设
 */
function delPersonality(){
  if(personalityId === -1){
    ElMessage.warning('当前无人设，无需删除')
    return
  }
  const userId = userStore.id
  personality.delPersonality(personalityId, userId).then(response => {
    if (response.code === 200){
      updatePersonalityId()
      handlePrompt()
    }
  })
}

/**
 * 查看人设
 */
async function fetchPersonality() {
  const userId = userStore.id;
  personality.getPersonality(personalityId, userId).then(response => {
    if (response.code === 200){
      if(response.hasOwnProperty('data')){
        ElMessageBox.alert(response.data.description, '角色设定', {
          confirmButtonText: '知悉',
        })
      }else{
       ElMessage.info('当前没有人设')
      }
    } else {
      console.log(response);
    }
  })
}

/**
 * 重构人设
 */
function rebuildPersonality(){
  const userId = userStore.id;
  if(!Array.isArray(promptHistory.value) || (Array.isArray(promptHistory.value) && promptHistory.value.length === 0)){
    ElMessage.error('当前没有人设描述')
    return
  }
  isPromptLoading.value = true
  personality.rebuildPersonality(personalityId, userId).then(response => {
    if (response.code === 200){
      ElMessage.info('重构人设成功')
    }else{
      ElMessage.info('重构人设失败')
    }
    isPromptLoading.value = false
  })
}

/**
 * 更新当前用户人设id
 */
function updatePersonalityId() {
  return new Promise((resolve, reject) => {
    const userId = userStore.id;
    personality.getPersonalityId(userId).then(response => {
      if (response.code === 200) {
        personalityId = response.data;
        resolve();
      } else {
        console.log("人设id不存在");
        reject(new Error("人设id不存在"));
      }
    }).catch(error => {
      reject(error);
    });
  });
}

/**
 * @name: 处理回车键
 * @description: 处理回车键发送消息
 * @return {*}
 */
function handleEnter() {
  submitChatMessage()
}

/**
 * @name: 处理Shift+Enter键
 * @description: 处理Shift+Enter键换行
 * @return {*}
 */
function handleShiftEnter(event) {
  // event.target.value += '\n'
  formData.value.msgInput = event.target.value
}

function formattedDate(dateStr){
  const date = new Date(dateStr);
  const year = date.getFullYear();
  const month = date.getMonth() + 1;
  const day = date.getDate();
  return `${year}/${month}/${day}`;
}

function formattedTime(dateStr) {
  const date = new Date(dateStr);
  const year = date.getFullYear();
  const month = date.getMonth() + 1;
  const day = date.getDate();
  const hours = date.getHours();
  const minutes = date.getMinutes();
  const seconds = date.getSeconds();

  // 确保月份、日期、小时、分钟和秒都是两位数
  const paddedMonth = month < 10 ? `0${month}` : month;
  const paddedDay = day < 10 ? `0${day}` : day;
  const paddedHours = hours < 10 ? `0${hours}` : hours;
  const paddedMinutes = minutes < 10 ? `0${minutes}` : minutes;
  const paddedSeconds = seconds < 10 ? `0${seconds}` : seconds;

  return `${year}/${paddedMonth}/${paddedDay} ${paddedHours}:${paddedMinutes}:${paddedSeconds}`;
}

/**
 * 页面加载时调用
 */
onMounted(() => {
  handlePrompt()
  handleChatHistory()
  updatePersonalityId()
})

</script>

<template>
  <div class="common-layout">
    <el-container class="main-container">
      <el-header class="button-header">
        <el-button-group class="button-group-header">
          <el-button @click="fetchPersonality" type="success">
            查看人设<el-icon><Avatar /></el-icon>
          </el-button>
          <el-popconfirm title="你确定要重构吗?" @confirm="rebuildPersonality" width="160px">
            <template #reference>
              <el-button type="warning">
                重构人设<el-icon><HelpFilled /></el-icon>
              </el-button>
            </template>
          </el-popconfirm>
          <el-popconfirm title="你确定要删除吗?" @confirm="delPersonality" width="160px">
            <template #reference>
              <el-button type="danger">
                删除人设<el-icon><DeleteFilled /></el-icon>
              </el-button>
            </template>
          </el-popconfirm>
        </el-button-group>
      </el-header>
      <el-container class="prompt-chat-component">
        <el-aside class="prompt-aside" v-loading="isPromptLoading" element-loading-text="正在构建智能体...">
          <el-scrollbar class="prompt-scrollbar">
            <el-timeline class="prompt-timeline">
              <el-timeline-item class="prompt-item" center v-for="item in promptHistory" :timestamp="item.date" placement="top">
                <el-card class="prompt-card">
                  <span class="prompt-text" :data-content="item.prompt">{{ item.prompt }}</span>
                  <el-button
                      type="danger"
                      :icon="Delete"
                      circle
                      @click="deletePrompt(item.id)">
                  </el-button>
                </el-card>
              </el-timeline-item>
            </el-timeline>
          </el-scrollbar>
        </el-aside>
        <el-main class="chat-main" style="overflow: unset">
          <div class="red-line"></div>
          <div class="bubble-div" style="left: 70% ;top: 60%"></div>
          <div class="bubble-div" style="animation: none; width: 100px; height: 100px"></div>
          <el-scrollbar class="chat-history">
            <div v-for="(msg, index) in chatHistory" :key="index" class="chat-message" :class="{'user-message': msg.role === 'user', 'bot-message': msg.role === 'bot', 'wait-message': msg.role === 'wait'}" v-loading="msg.role === 'wait'">
              <el-avatar class="chat-avatar" :size="'default'" :shape="'circle'" :src="msg.role === 'user' ? userStore.avatar : botAvatar" :fit="'contain'"></el-avatar>
              <div class="message-body">
                <span class="chat-timestamp">{{msg.date}}</span>
                <v-md-preview class="message-content" :text="msg.content"></v-md-preview>
              </div>
            </div>
          </el-scrollbar>
        </el-main>
      </el-container>
      <el-footer class="input-button-footer">
        <el-form class="chat-input-form" ref="formRef" :model="formData">
          <el-form-item>
            <el-input class="message-input" v-model="formData.msgInput" type="textarea" placeholder="请先为我添加设定描述再和我对话...(Shift + Enter换行,按下Enter发送对话)"
                      resize="none" :autosize="{minRows: 1, maxRows: 5}"
                      @keydown.enter.exact.prevent="handleEnter"
                      @keydown.enter.shift.exact="handleShiftEnter"></el-input>
          </el-form-item>
          <el-form-item class="button-group-item">
            <el-button-group class="button-group-footer">
              <el-button type="primary" @click="submitPrompt" size="large">
                添加描述<el-icon><CirclePlusFilled /></el-icon>
              </el-button>
              <el-button type="primary" @click="submitChatMessage" size="large">
                对话<el-icon><ChatRound /></el-icon>
              </el-button>
              <el-popconfirm title="你确定要重置吗?" @confirm="resetChat" width="160px">
                <template #reference>
                  <el-button type="warning" size="large">
                    重置对话<el-icon><Refresh /></el-icon>
                  </el-button>
                </template>
              </el-popconfirm>
            </el-button-group>
          </el-form-item>
        </el-form>
      </el-footer>
    </el-container>
  </div>
</template>

<style scoped lang="scss">
.bubble-div{
  position: absolute;
  width: 150px;
  height: 150px;
  background: transparent;
  border-radius: 57% 43% 37% 63% / 45% 52% 48% 52%;
  box-shadow: inset 10px 10px 10px rgba(0, 0, 0, 0.05),
  15px 25px 10px rgba(0, 0, 0, 0.1),
  15px 20px 20px rgba(0, 0, 0, 0.05),
  inset -10px -10px 15px rgba(237, 237, 237, 0.9);
  animation: anim 1s infinite alternate;
}
.background-div:before{
  content: '';
  position: absolute;
  left: 25px;
  top: 35px;
  width: 20px;
  height: 20px;
  border-radius: 42% 58% 37% 63% / 40% 43% 57% 60%;
  background: #FFF
}
.background-div:after{
  content: '';
  position: absolute;
  left: 60px;
  top: 25px;
  width: 10px;
  height: 10px;
  border-radius: 42% 58% 37% 63% / 40% 43% 57% 60%;
  background: #ffffff
}
@keyframes anim {
  0% {
    transform: translateY(0);
    height: 150px;
    width: 150px;
  }

  100% {
    transform: translateY(40px);
    height: 135px;
    width: 160px;
  }
}

.common-layout{
  width: 100%;
  height: calc(100vh - 84px);
  background-color: #e8e8e8;
}
.prompt-chat-component{
  height: 65vh;
  width: 100%;
}
/**
  container
 */
.main-container{
  margin-left: 0 !important;
  padding: 10px 20px 20px 20px;
}
/**
  header
 */
.button-header{
  width: 100%;
  height: auto;
  margin-bottom: 10px;
}
.button-group-header {
  width: 100%;
  display: flex;
  justify-content: center;
}

/**
  aside
 */
.prompt-aside{
  width: 40%;
  --dark-grey: #353535;
  --middle-grey: #767676;
  --lightest-grey: linear-gradient(#fafafa,#ebebeb);
  --shadow: 0 5px 15px 0 #00000026;
  --shadow-active: 0 5px 5px 0 #00000026;
  --border-radius-main: 10px;
  --border-radius-icon: 50px;
  cursor: default;
  color: var(--dark-grey);
  opacity: .9;
  padding: 1.5rem;
  font-weight: 600;
  background: var(--lightest-grey);
  border-radius: var(--border-radius-main);
  box-shadow: var(--shadow);
  transition: .2s ease all;
}
.prompt-aside:hover{
  box-shadow: var(--shadow-active);
}
.prompt-scrollbar{
  width: 100%;
}
.prompt-timeline{
  padding-left: 5px;
}
.prompt-item{

}
.prompt-card :deep(.el-card__body){
  display: flex;
}
.prompt-text {
  display: flex;
  width: 100%;
  flex-grow: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}
.prompt-text[data-content]:hover::after {
  content: attr(data-content);
  position: absolute;
  background-color: #333;
  color: #fff;
  padding: 5px;
  border-radius: 3px;
  white-space: normal;
  z-index: 10;
}

/**
  main
 */
.chat-main{
  width: 60%;
  height: calc(100% - 1rem);
  position: relative;
  box-sizing: border-box;
  font-family: cursive;
  font-size: 20px;
  border-radius: 10px;
  background: #fff;
  background-image: linear-gradient(#f5f5f0 1.1rem, #ccc 1.2rem);
  background-size: 100% 1.2rem;
  line-height: 1.2rem;
  padding-left: 0.6rem;
  margin-left: 1rem;
  margin-bottom: 1rem;
}
.chat-main::before,
.chat-main::after{
  position: absolute;
  content: "";
  bottom: 10px;
  width: 40%;
  height: 10px;
  box-shadow: 0 5px 14px rgba(0, 0, 0, 0.7);
  z-index: -1;
  transition: all 0.3s ease;
}
.chat-main::before{
  left: 15px;
  transform: skew(-5deg) rotate(-5deg);
}
.chat-main::after{
  right: 15px;
  transform: skew(5deg) rotate(5deg);
}
.chat-main:hover::before,
.chat-main:hover::after{
  box-shadow: 0 2px 14px rgba(0, 0, 0, 0.4);
}
.red-line{
  position: absolute;
  border-left: 1px solid #d88;
  height: 100%;
  left: 3.3rem;
  top: 0;
}

.chat-history{
}
.chat-message{
  margin-bottom: 1rem;
}
.chat-message :deep(.el-loading-mask){
  background: none!important;
}
.chat-avatar{
  background: none!important;
}
.message-body{
  max-width: 75%;
}
.chat-timestamp{
  color: var(--el-text-color-secondary);
  line-height: 1;
  font-size: var(--el-font-size-small);
}
.message-content{
  word-wrap: break-word;
  overflow-wrap: break-word;
  width: fit-content;
  min-width: 100%;
  max-width: 75%;
  color: black;
  align-items: center;
  gap: 20px;
  backdrop-filter: blur(1px);
}
.message-content :deep(.github-markdown-body){
  padding: 0!important;
}

/* 用户消息样式 */
.user-message {
  display: flex;
  flex-direction: row-reverse;
  justify-content: flex-start;
  align-items: flex-start;
}

.user-message .chat-avatar {
  margin-left: 10px; /* 头像与消息内容的间距 */
}

.user-message .message-body {
  text-align: left;
}

.user-message .chat-timestamp {
  display: block;
  text-align: right;
}

/* 机器人消息样式 */
.bot-message, .wait-message {
  display: flex;
  flex-direction: row;
  justify-content: flex-start;
  align-items: flex-start;
}

.bot-message .chat-avatar, .wait-message .chat-avatar{
  margin-right: 10px; /* 头像与消息内容的间距 */
}

.bot-message .message-body, .wait-message .message-body {
  text-align: left;
}

.bot-message .chat-timestamp, .wait-message .chat-timestamp {
  display: block;
  text-align: left;
}

.message-input :deep(.el-textarea__inner){
  width: 100%;
  padding: 10px;
  border: 2px solid #ccc;
  border-radius: 5px;
  font-size: 16px;
  color: #555;
  outline: none;
}
.message-input :deep(.el-textarea__inner):focus{
  border-color: #007bff;
  box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
/**
  footer
 */
.input-button-footer{
  height: auto;
}
.chat-input-form{

}
.button-group-item{
}
.button-group-footer{
  width: 100%;
  display: flex;
  justify-content: center;
}
</style>
